package com.ushine.web.component.mybatis.desensitization.plugin;

import com.ushine.web.component.mybatis.desensitization.SensitiveTypeEnum;
import com.ushine.web.component.mybatis.desensitization.annotation.Desensitization;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Stream;

/**
 * @Author: yls
 * @Date: 2022/02/13 20:17
 * @Description: 数据脱敏处理器：自定义插件
 * @Version 1.0
 */
@Slf4j
@Intercepts(
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
)
public class DesensitizationPlugin implements Interceptor {

    @Resource
    private DesensitizationFunManager desensitizationFunManager;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        List<Object> records = (List<Object>) invocation.proceed();
        // 对结果集脱敏
        records.forEach(this::sensitive);
        return records;
    }


    /**
     * 功能描述:
     * 〈 捕捉属性上的标记注解@Sensitive 并进行对应的脱敏处理 〉
     *
     * @param source 原始对象
     * @return : void
     * @author : yls
     * @date : 2022/02/13 20:34
     */
    private void sensitive(Object source) {
        // 获取返回值了开心
        Class<?> sourceClass = source.getClass();
        // 初始化返回值的MetaObject
        MetaObject metaObject = SystemMetaObject.forObject(source);
        // 捕捉到属性上的标记注解 @Sensitive 并进行对应的脱敏处理
        Stream.of(sourceClass.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(Desensitization.class))
                .forEach(field -> doSensitive(metaObject, field));
    }


    /**
     * 功能描述:
     * 〈 具体的字段脱敏处理类 〉
     *
     * @param metaObject 脱敏类
     * @param field      字段名称
     * @return : void
     * @author : yls
     * @date : 2022/02/13 20:36
     */
    private void doSensitive(MetaObject metaObject, Field field) {
        // 属性名
        String name = field.getName();
        // 获取属性值
        Object value = metaObject.getValue(name);
        // 只有字符串才进行脱敏，而且不能为NULL
        if (String.class == metaObject.getGetterType(name) && value != null) {
            Desensitization annotation = field.getAnnotation(Desensitization.class);
            // 获取对应的脱敏策略，并进行脱敏
            SensitiveTypeEnum type = annotation.type();
            String result = desensitizationFunManager.getResultByTypeName(type, (String) value);
            // 把脱敏的值放回去
            metaObject.setValue(name, result);
        }
    }
}
